//
//  WmcLocalMediaAlbumDBManager.swift
//  YPWatermarkCamera
//
//  Created by 赵新 on 2022/7/12.
//

import UIKit
import YPBedrock
import YPNetwork
import AVFoundation

typealias DBMediaInfoModel = WmcLocalMediaAlbumDBManager.DBModel.MediaInfoModel

public extension Array where Element: WmcLocalMediaAlbumDBManager.DBModel {
    /// 转换成媒体数据列表
    var asWmcMediaList: [WmcMedia] {
        var mediaList: [WmcMedia] = []
        for dbModel in self {
            if let filePath = dbModel.mediaInfoType.filePath {
                switch dbModel.mediaType {
                case .image:
                    let image = WmcImage(filePath: filePath)
                    mediaList.append(image)
                case .video:
                    let video = WmcVideo(filePath: filePath)
                    mediaList.append(video)
                default: break
                }
            }
        }
        return mediaList
    }
}

// MARK: - [WmcLocalMediaAlbumDBManager.DBModel]

public extension WmcLocalMediaAlbumDBManager {
    /// 数据库模型
    class DBModel: NSObject {
        /// 媒体信息模型
        struct MediaInfoModel {
            var path: String
            var relativePath: String
            var watermarkJson: String
        }

        // Lifecycle

        public override init() {
            super.init()
        }

        // Internal

        enum MediaInfoType {
            case unknown
            case image(info: MediaInfoModel)
            case jigsaw(info: MediaInfoModel)
            case gif(info: MediaInfoModel)

            case video(info: MediaInfoModel)
            case movieAlbum(info: MediaInfoModel)
            case slowVideo(info: MediaInfoModel)

            /// 文件路径
            var filePath: String? {
                switch self {
                case .unknown: return nil
                case let .image(info: info): return info.path
                case let .jigsaw(info: info): return info.path
                case let .gif(info: info): return info.path
                case let .video(info: info): return info.path
                case let .movieAlbum(info: info): return info.path
                case let .slowVideo(info: info): return info.path
                }
            }

            var relativePath: String? {
                switch self {
                case .unknown: return nil
                case let .image(info: info): return info.relativePath
                case let .jigsaw(info: info): return info.relativePath
                case let .gif(info: info): return info.relativePath
                case let .video(info: info): return info.relativePath
                case let .movieAlbum(info: info): return info.relativePath
                case let .slowVideo(info: info): return info.relativePath
                }
            }

            /// 水印信息
            var watermarkJson: String? {
                switch self {
                case .unknown: return nil
                case let .image(info: info): return info.watermarkJson
                case let .jigsaw(info: info): return info.watermarkJson
                case let .gif(info: info): return info.watermarkJson
                case let .video(info: info): return info.watermarkJson
                case let .movieAlbum(info: info): return info.watermarkJson
                case let .slowVideo(info: info): return info.watermarkJson
                }
            }
        }

        var id: Int = 0
        var userId: Int = 0
        var mediaType: WmcMedia.MediaType = .unknown
        var previewImagePath: String = ""
        var previewImagerelativePath: String = ""
        var saveTime: Date = .init()

        var mediaInfoType: MediaInfoType = .unknown

        func mapping(_ dic: [String: Any]) {
            typealias DB = WmcLocalMediaAlbumDBManager
            let sandBoxPath = YPSystem.Documents.Path
            if let id = dic[DB.dataType[0].name] as? Int {
                self.id = id
            }
            if let userId = dic[DB.dataType[1].name] as? Int {
                self.userId = userId
            }
            if let mediaType = dic[DB.dataType[2].name] as? Int {
                self.mediaType = .init(rawValue: mediaType) ?? .unknown
            }
            if let previewImagePath = dic[DB.dataType[3].name] as? String {
                self.previewImagerelativePath = previewImagePath
                self.previewImagePath = sandBoxPath.yp.appendPathComponent(previewImagePath)
            }
            if let saveTimeInterval = dic[DB.dataType[4].name] as? TimeInterval {
                self.saveTime = .init(timeIntervalSince1970: saveTimeInterval)
            }
            // 图片
            if let path = dic[DB.dataType[5].name] as? String,
               !path.isEmpty,
               let wmJson = dic[DB.dataType[6].name] as? String {
                let _path = sandBoxPath.yp.appendPathComponent(path)
                self.mediaInfoType = .image(info: .init(path: _path, relativePath: path, watermarkJson: wmJson))
            }

            if let path = dic[DB.dataType[7].name] as? String,
               !path.isEmpty,
               let wmJson = dic[DB.dataType[8].name] as? String {
                let _path = sandBoxPath.yp.appendPathComponent(path)
                self.mediaInfoType = .jigsaw(info: .init(path: _path, relativePath: path, watermarkJson: wmJson))
            }

            if let path = dic[DB.dataType[9].name] as? String,
               !path.isEmpty,
               let wmJson = dic[DB.dataType[10].name] as? String {
                let _path = sandBoxPath.yp.appendPathComponent(path)
                self.mediaInfoType = .gif(info: .init(path: _path, relativePath: path, watermarkJson: wmJson))
            }
            // 视频
            if let path = dic[DB.dataType[11].name] as? String,
               !path.isEmpty,
               let wmJson = dic[DB.dataType[12].name] as? String {
                let _path = sandBoxPath.yp.appendPathComponent(path)
                self.mediaInfoType = .video(info: .init(path: _path, relativePath: path, watermarkJson: wmJson))
            }

            if let path = dic[DB.dataType[13].name] as? String,
               !path.isEmpty,
               let wmJson = dic[DB.dataType[14].name] as? String {
                let _path = sandBoxPath.yp.appendPathComponent(path)
                self.mediaInfoType = .movieAlbum(info: .init(path: _path, relativePath: path, watermarkJson: wmJson))
            }

            if let path = dic[DB.dataType[15].name] as? String,
               !path.isEmpty,
               let wmJson = dic[DB.dataType[16].name] as? String {
                let _path = sandBoxPath.yp.appendPathComponent(path)
                self.mediaInfoType = .slowVideo(info: .init(path: _path, relativePath: path, watermarkJson: wmJson))
            }
        }
    }
}

// MARK: - WmcLocalMediaAlbumDBManager + WmcDBProtocol

extension WmcLocalMediaAlbumDBManager: WmcDBProtocol {
    /// 表名
    static var tableName: String = "local_media_album"

    /// 表头数据列
    static var dataType: [WmcDBManager.DataType] {
        return [
            .primaryKey(), // 主键
            .integer(name: "user_id"), // 用户信息
            .integer(name: "media_type"), // 媒体类型
            .varchar(name: "preview_image_path"), // 预览图地址
            .date(name: "save_time"), // 存储时间
            // 图片媒体
            .varchar(name: "image_path"), // 原图地址
            .varchar(name: "image_watermark_info"), // json格式的字符串水印数据
            // 拼图媒体（预留，大概率）
            .varchar(name: "jigsaw_path"), // 拼图地址
            .varchar(name: "jigsaw_watermark_info"), // json格式的字符串水印数据 (拼图可能存在多组水印)
            // GIF媒体（预留，小概率）
            .varchar(name: "gif_path"), // GIF地址
            .varchar(name: "gif_watermark_info"), // json格式的字符串水印数据

            // 视频媒体
            .varchar(name: "video_path"), // 视频地址
            .varchar(name: "video_watermark_info"), // json格式的字符串水印数据
            // 影集媒体（预留，中概率）
            .varchar(name: "movie_album_path"), // 影集地址
            .varchar(name: "movie_album_watermark_info"), // json格式的字符串水印数据 (影集可能存在多组水印)
            // 慢动作媒体（预留，小概率）
            .varchar(name: "slow_video_path"), // 慢动作地址
            .varchar(name: "slow_video_watermark_info"), // json格式的字符串水印数据（可能存在音频混合水印）
        ]
    }

    /// 数据表迁移执行
    static func migration() {
        typealias OldDB = WmcLocalAlbumDBManager
        // 检查表存在
        WmcDBManager.shared.existTable(tableName: OldDB.tableName) { result in
            guard result else { return } // 存在则迁移
            // 数据迁移
            OldDB.searchImageData() { modelList in
                modelList.reversed().forEach { model in
                    // 调用存储
                    let insertData: [WmcDBManager.InsertData] = [
                        .init(column: dataType[1], value: model.userId),
                        .init(column: dataType[2], value: WmcMedia.MediaType.image.rawValue),
                        .init(column: dataType[3], value: model.thumbnailImagePath),
                        .init(column: dataType[4], value: model.saveTime),
                        .init(column: dataType[5], value: model.originlImagePath),
                        .init(column: dataType[6], value: model.watermarkInfo ?? ""),
                        .init(column: dataType[7], value: ""),
                        .init(column: dataType[8], value: ""),
                        .init(column: dataType[9], value: ""),
                        .init(column: dataType[10], value: ""),
                        .init(column: dataType[11], value: ""),
                        .init(column: dataType[12], value: ""),
                        .init(column: dataType[13], value: ""),
                        .init(column: dataType[14], value: ""),
                        .init(column: dataType[15], value: ""),
                        .init(column: dataType[16], value: ""),
                    ]
                    WmcDBManager.shared.insertData(tableName: tableName, insertData: insertData)
                }
                if let block = migrationCompleteBlock {
                    block()
                    migrationCompleteFlag = true
                } else {
                    migrationCompleteFlag = false
                }
                // 迁移完成后删除表
                WmcDBManager.shared.deleteTable(tableName: OldDB.tableName)
            }
        }
    }

    static func setMigrationCompleteBlock(_ block: @escaping () -> Void) {
        migrationCompleteBlock = block
        if let migrationCompleteFlag = migrationCompleteFlag,
           !migrationCompleteFlag { // 迁移完成但未回调
            migrationCompleteBlock?()
        }
    }
}

// MARK: - [WmcLocalMediaAlbumDBManager]

public struct WmcLocalMediaAlbumDBManager {
    // Internal

    /// 媒体文件夹名
    static var localMediaAlbumFolderName: String = "WmcLocalMediaAlbum"

    /// 文件管理器
    static var fileManager: FileManager = .default

    /// 迁移完成回调
    static var migrationCompleteBlock: YPBlock.Empty?

    /// 迁移回调标记
    static var migrationCompleteFlag: Bool?

    /// 插入图片数据
    /// - Parameters:
    ///   - image: 图片
    ///   - watermarkInfo: 水印信息
    ///   - resultBlock: 结果
    static func insertImage(_ image: WmcImage,
                            watermarkJson: String,
                            resultBlock: YPBlock.FourParam<WmcMedia.MediaType, String?, String, DBMediaInfoModel>? = nil)
    {
        image.loadUIImage { image in
            // 图片数据处理
            guard let imageData = image.yp.jpegData(compressed: 0.7),
                  let previewImageData = image.yp.set(squareMode: .center)?
                  .yp.setScale(ratio: 0.1)?
                  .yp.pngData else { return }

            let groupFolderPath = groupFolder()
            let key = "\(Date.timeIntervalBetween1970AndReferenceDate.yp.int)_\(String.YP.RandomString(32))"
            // 预览图 原图 路径
            let previewImagePath = groupFolderPath.yp.appendPathComponent("image_preview_\(image.hashValue)_\(key)")
            let imagePath = groupFolderPath.yp.appendPathComponent("image_\(image.hashValue)_\(key)")
            // 建好文件夹
            let createPath = YPSystem.Documents.Path.yp.appendPathComponent(groupFolderPath)
            try? fileManager.yp.createDirectory(createPath)
            // 文件完整路径
            let previewImageWritePath = YPSystem.Documents.Path.yp.appendPathComponent(previewImagePath)
            let imageWritePath = YPSystem.Documents.Path.yp.appendPathComponent(imagePath)
            // 写入图片
            try? previewImageData.write(to: previewImageWritePath.yp.fileUrl)
            try? imageData.write(to: imageWritePath.yp.fileUrl)
            // 调用存储
            let insertData: [WmcDBManager.InsertData] = [
                .init(column: dataType[5], value: imagePath),
                .init(column: dataType[6], value: watermarkJson),

                .init(column: dataType[7], value: ""),
                .init(column: dataType[8], value: ""),
                .init(column: dataType[9], value: ""),
                .init(column: dataType[10], value: ""),
                .init(column: dataType[11], value: ""),
                .init(column: dataType[12], value: ""),
                .init(column: dataType[13], value: ""),
                .init(column: dataType[14], value: ""),
                .init(column: dataType[15], value: ""),
                .init(column: dataType[16], value: ""),
            ]
            insertMediaGeneralColumn(mediaType: .image,
                                     previewImagePath: previewImagePath) {
                return insertData
            } resultBlock: { mediaType in
                let info: DBMediaInfoModel = .init(path: imageWritePath, relativePath: imagePath, watermarkJson: watermarkJson)
                resultBlock?(.image, previewImageWritePath, previewImagePath, info)
            }
        }
    }

    /// 插入视频数据
    /// - Parameters:
    ///   - video: 视频
    ///   - watermarkInfo: 水印信息
    ///   - resultBlock: 结果
    static func insertVideo(_ video: WmcVideo,
                            watermarkJson: String,
                            resultBlock: YPBlock.FourParam<WmcMedia.MediaType, String?, String, DBMediaInfoModel>? = nil)
    {
        video.loadAVAsset { avAsset in
            // 视频路径
            var videoOriginalPath = ""
            switch video.videoType {
            case let .url(url):
                let fileUrl = URL.YP.Create(url)
                videoOriginalPath = fileUrl.absoluteString
            case let .filePath(filePath): videoOriginalPath = filePath
            default: break
            }
            videoOriginalPath = videoOriginalPath.yp.removeFileUrlPrefix()
            // 获取视频封面帧
            let imageGenerator = AVAssetImageGenerator(asset: avAsset)
            imageGenerator.requestedTimeToleranceAfter = .zero
            imageGenerator.requestedTimeToleranceBefore = .zero
            let time: CMTime = .init(value: 1, timescale: 1)
            var actualTime: CMTime = .init()
            guard let coverCGImage = try?
                imageGenerator.copyCGImage(at: time, actualTime: &actualTime) else { return }

            let previewImage = UIImage(cgImage: coverCGImage)
            // 图片数据处理
            var angle: CGFloat = 0
            switch WmcMotionManager.shared.orientation.value {
            case .landscapeRight: angle = 90 // 右
            case .portraitUpsideDown: angle = 180 // 反向
            case .landscapeLeft: angle = 270 // 左
            default: break
            }
            guard let previewImageData = previewImage
                .yp.set(squareMode: .center)?
                .yp.setScale(ratio: 0.3)?
                .yp.setRotate(angle: angle)?
                .yp.pngData else { return }

            let groupFolderPath = groupFolder()
            // 预览图路径
            let previewImagePath = groupFolderPath.yp.appendPathComponent("video_preview_\(previewImageData.hashValue)")
            let videoPath = groupFolderPath.yp.appendPathComponent("video_\(previewImageData.hashValue).mp4")
            // 建好文件夹
            let createPath = YPSystem.Documents.Path.yp.appendPathComponent(groupFolderPath)
            try? fileManager.yp.createDirectory(createPath)
            // 写入路径
            let previewImageWritePath = YPSystem.Documents.Path.yp.appendPathComponent(previewImagePath)
            let videoMovePath = YPSystem.Documents.Path.yp.appendPathComponent(videoPath)
            // 写入
            try? previewImageData.write(to: previewImageWritePath.yp.fileUrl)
            try? fileManager.moveItem(atPath: videoOriginalPath, toPath: videoMovePath)
            try? fileManager.removeItem(atPath: videoOriginalPath) // 删除文件
            // 调用存储
            let insertData: [WmcDBManager.InsertData] = [
                .init(column: dataType[5], value: ""),
                .init(column: dataType[6], value: ""),
                .init(column: dataType[7], value: ""),
                .init(column: dataType[8], value: ""),
                .init(column: dataType[9], value: ""),
                .init(column: dataType[10], value: ""),

                .init(column: dataType[11], value: videoPath),
                .init(column: dataType[12], value: watermarkJson),

                .init(column: dataType[13], value: ""),
                .init(column: dataType[14], value: ""),
                .init(column: dataType[15], value: ""),
                .init(column: dataType[16], value: ""),
            ]
            insertMediaGeneralColumn(mediaType: .video,
                                     previewImagePath: previewImagePath) {
                return insertData
            } resultBlock: { mediaType in
                let info: DBMediaInfoModel = .init(path: videoMovePath, relativePath: videoPath, watermarkJson: watermarkJson)
                resultBlock?(.image, previewImageWritePath, previewImagePath, info)
            }
        }
    }

    /// 插入通用列数据
    /// - Parameters:
    ///   - mediaType: 媒体类型
    ///   - previewImagePath: 预览图路径
    ///   - insertDataBlock: 插入数据
    ///   - resultBlock: 结果
    static func insertMediaGeneralColumn(mediaType: WmcMedia.MediaType,
                                         previewImagePath: String?,
                                         insertDataBlock: YPBlock.Return<[WmcDBManager.InsertData]>,
                                         resultBlock: @escaping YPBlock.OneParam<WmcMedia.MediaType>)
    {
        // 用户信息
        var userId: Int = 0
        if let _userId = WmcLoginManager.shared.yupaoAccountModel?.userId, !_userId.isEmpty {
            userId = _userId.yp.int
        }
        let _previewImagePath = previewImagePath ?? ""
        // 日期文件夹
        let saveTimeInterval = saveDate().timeIntervalSince1970.yp.int
        // 调用数据库
        var insertData: [WmcDBManager.InsertData] = [
            .init(column: dataType[1], value: userId),
            .init(column: dataType[2], value: mediaType.rawValue),
            .init(column: dataType[3], value: _previewImagePath),
            .init(column: dataType[4], value: saveTimeInterval),
        ]
        insertData.append(contentsOf: insertDataBlock())
        WmcDBManager.shared.insertData(tableName: Self.tableName, insertData: insertData) { result in
            if result {
                resultBlock(mediaType)
            }
        }
    }

    /// 读取搜索数据
    /// - Parameters:
    ///   - page: 分页
    ///   - pageSize: 分页大小
    ///   - resultBlock: 返回Block
    static func searchMediaData(page: Int = 1,
                                pageSize: Int = 200,
                                resultBlock: YPBlock.OneParam<[DBModel]>? = nil) {
        WmcDBManager.shared.searchData(tableName: tableName, page: page, pageSize: pageSize, isDesc: true) { dicList in
            var imageList: [DBModel] = []
            dicList.forEach { dic in
                let model: DBModel = .init()
                model.mapping(dic)
                imageList.append(model)
            }
            resultBlock?(imageList)
        }
    }

    /// 删除媒体资源
    /// - Parameters:
    ///   - id: id
    ///   - resultBlock: 结果
    static func deleteMedia(id: Int,
                            resultBlock: YPBlock.OneParam<Bool>? = nil) {
        // 找到数据，删除原文件
        WmcDBManager.shared.searchData(tableName: tableName, id: id, resultBlock: { dic in
            guard let dic = dic else { return }
            let model: DBModel = .init()
            model.mapping(dic)
            // 删除预览
            try? fileManager.removeItem(atPath: model.previewImagePath)
            // 删除原文件
            if let filePath = model.mediaInfoType.filePath {
                try? fileManager.removeItem(atPath: filePath)
            }
            // 删除数据库数据
            WmcDBManager.shared.deleteData(tableName: tableName, id: id, resultBlock: resultBlock)
        })
    }

    // Private

    /// 保存时间
    private static func saveDate() -> Date {
        var saveDate = Date()
        if let networkDate = WmcDateManager.shared.networkDate.value {
            saveDate = networkDate
        }
        return saveDate
    }

    /// 组文件夹
    private static func groupFolder() -> String {
        let date = saveDate()
        let datePath = "\(date.yp.year)/\(date.yp.month)/\(date.yp.day)"
        let groupFolder = localMediaAlbumFolderName.yp.appendPathComponent(datePath)
        return groupFolder
    }
}
